package ddz.utils;

import ch.qos.logback.classic.Logger;
import com.kaka.util.Charsets;
import com.kaka.util.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * HTTP请求工具类
 *
 * @author zhoukai
 */
public class HttpUtils {

    private static final Logger logger = (Logger) LoggerFactory.getLogger(HttpUtils.class);

    private static Registry<ConnectionSocketFactory> socketFactoryRegistry;

    /**
     * 忽略SSL验证的SSL上下文对象
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContexts.custom().build();
        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

    static {
        SSLContext sslcontext = null;
        try {
            sslcontext = createIgnoreVerifySSL();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            logger.error(e.getMessage(), e);
        }
        if (sslcontext != null) {
            socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", new SSLConnectionSocketFactory(sslcontext))
                    .build();
        }
    }

    /**
     * 创建Http请求对象
     *
     * @param ssl false无ssl的请求，true为忽略ssl的请求
     * @return http请求对象
     */
    public static CloseableHttpClient createClient(boolean ssl) {
        if (ssl) {
            return HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager(socketFactoryRegistry)).build();
        }
        return HttpClients.createDefault();
    }

    public static String synGet(String url) {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            try {
                RequestBuilder requestBuilder = RequestBuilder.get(new URI(url));
                HttpUriRequest httpRequest = requestBuilder.build();
                try (CloseableHttpResponse response = httpclient.execute(httpRequest)) {
                    int statusCode = response.getStatusLine().getStatusCode();
                    if (statusCode == 200) {
                        HttpEntity resEntity = response.getEntity();
                        if (resEntity != null) {
                            String resString = EntityUtils.toString(resEntity, Charsets.utf8);
                            EntityUtils.consume(resEntity);
                            return resString;
                        }
                    } else {
                        logger.error("HTTP返回错误码" + statusCode);
                    }
                }
            } catch (URISyntaxException ex) {
                logger.error(ex.getMessage(), ex);
            }
        } catch (NoHttpResponseException ex) {
            logger.error(ex.getMessage());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }

    /**
     * 同步方式提交字节流数据到HTTP服务器
     *
     * @param url
     * @param data          提交的数据
     * @param requestConfig 请求配置，为null表示使用组件默认的请求配置
     * @return HTTP服务器返回的数据
     */
    public static byte[] synPostByteStream(String url, byte[] data, RequestConfig requestConfig) {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            try {
                RequestBuilder rb = RequestBuilder.post()
                        .setUri(new URI(url));
                if (data != null) {
                    rb.setHeader("Content-Type", "application/octet-stream");
                    rb.setEntity(new ByteArrayEntity(data, ContentType.APPLICATION_OCTET_STREAM));
                }
                if (requestConfig != null) {
                    rb.setConfig(requestConfig);
                }
                HttpUriRequest httpRequest = rb.build();
                try (CloseableHttpResponse response = httpclient.execute(httpRequest)) {
                    int statusCode = response.getStatusLine().getStatusCode();
                    if (statusCode == 200) {
                        HttpEntity resEntity = response.getEntity();
                        if (resEntity != null) {
                            byte[] bytes = EntityUtils.toByteArray(resEntity);
                            EntityUtils.consume(resEntity);
                            return bytes;
                        }
                    } else {
                        logger.error("HTTP返回错误码" + statusCode);
                    }
                }
            } catch (URISyntaxException ex) {
                logger.error(ex.getMessage(), ex);
            }
        } catch (NoHttpResponseException ex) {
            logger.error(ex.getMessage());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }

    /**
     * 同步方式提交字节流数据到HTTP服务器
     *
     * @param url
     * @param data 提交的数据
     * @return HTTP服务器返回的数据
     */
    public static byte[] synPostByteStream(String url, byte[] data) {
        return synPostByteStream(url, data, null);
    }

    /**
     * 同步方式提交表单
     *
     * @param url
     * @param formBodyMap
     * @param action        表单提交方式， "get"、"post"
     * @param requestConfig 请求配置，为null表示使用组件默认的请求配置
     * @return HTTP服务器返回的数据
     */
    private static String synSubmitForm(String url, Map<String, Object> formBodyMap, String action, RequestConfig requestConfig) {
        //URL urlObj = new URL(url);
        //URI uri = urlObj.toURI();
        //System.out.println(uri.getScheme());
        //if ("https".equals(uri.getScheme())) {
        try (CloseableHttpClient httpclient = createClient(url.indexOf("https") == 0)) {
            try {
                RequestBuilder requestBuilder;
                switch (action) {
                    case "get":
                        requestBuilder = RequestBuilder.get(new URI(url));
                        break;
                    case "delete":
                        requestBuilder = RequestBuilder.delete(new URI(url));
                        break;
                    case "head":
                        requestBuilder = RequestBuilder.head(new URI(url));
                        break;
                    case "put":
                        requestBuilder = RequestBuilder.put(new URI(url));
                        break;
                    case "options":
                        requestBuilder = RequestBuilder.options(new URI(url));
                        break;
                    case "patch":
                        requestBuilder = RequestBuilder.patch(new URI(url));
                        break;
                    default:
                        requestBuilder = RequestBuilder.post(new URI(url));
                        break;
                }
                requestBuilder.setCharset(Charsets.utf8);
                //requestBuilder.addHeader(HTTP.CONTENT_TYPE, "text/html;charset=UTF-8");
                if (formBodyMap != null) {
                    formBodyMap.forEach((String name, Object formBody) -> {
                        requestBuilder.addParameter(name, formBody.toString());
                    });
                }
                if (requestConfig != null) {
                    requestBuilder.setConfig(requestConfig);
                }
                HttpUriRequest httpRequest = requestBuilder.build();
                //logger.info("数据请求：" + httpRequest.getRequestLine().getUri());
                try (CloseableHttpResponse response = httpclient.execute(httpRequest)) {
                    int statusCode = response.getStatusLine().getStatusCode();
                    if (statusCode == 200) {
                        HttpEntity resEntity = response.getEntity();
                        if (resEntity != null) {
                            byte[] bytes = EntityUtils.toByteArray(resEntity);
                            EntityUtils.consume(resEntity);
                            String resString = new String(bytes, Charsets.utf8);
                            return resString;
                        }
                    } else {
                        logger.error("HTTP返回错误码" + statusCode);
                    }
                }
            } catch (URISyntaxException ex) {
                logger.error(ex.getMessage(), ex);
            }
        } catch (NoHttpResponseException ex) {
            logger.error(ex.getMessage());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }

    /**
     * 同步方式提交表单
     *
     * @param url
     * @param formBodyMap
     * @param action      表单提交方式， "get"、"post"
     * @return HTTP服务器返回的数据
     */
    private static String synSubmitForm(String url, Map<String, Object> formBodyMap, String action) {
        return synSubmitForm(url, formBodyMap, action, null);
    }

    /**
     * 同步方式提交表单
     *
     * @param url
     * @param formBodyMap
     * @return HTTP服务器返回的数据
     */
    public static String synPostForm(String url, Map<String, Object> formBodyMap) {
        return synSubmitForm(url, formBodyMap, "post");
    }

    /**
     * 同步方式提交表单
     *
     * @param url
     * @param formBodyMap
     * @return HTTP服务器返回的数据
     */
    public static String synGetForm(String url, Map<String, Object> formBodyMap) {
        return synSubmitForm(url, formBodyMap, "get");
    }

    /**
     * 同步方式加载小文件
     *
     * @param url
     * @return
     */
    public static final byte[] synLoadFile(String url) {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            try {
                HttpUriRequest httpRequest = RequestBuilder.post()
                        .setUri(new URI(url))
                        .setHeader("Content-Type", "application/octet-stream")
                        .build();
                try (CloseableHttpResponse response = httpclient.execute(httpRequest)) {
                    int statusCode = response.getStatusLine().getStatusCode();
                    if (statusCode == 200) {
                        HttpEntity resEntity = response.getEntity();
                        if (resEntity != null) {
                            byte[] bytes = EntityUtils.toByteArray(resEntity);
                            EntityUtils.consume(resEntity);
                            return bytes;
                        }
                    } else {
                        return null;
                    }
                } catch (IOException ex) {
                    logger.error(ex.getMessage(), ex);
                } catch (Exception ex) {
                    logger.error(ex.getMessage(), ex);
                }
            } catch (URISyntaxException ex) {
                logger.error(ex.getMessage(), ex);
            }
        } catch (NoHttpResponseException ex) {
            logger.error(ex.getMessage());
        } catch (IOException ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }

    public static String getV4Ip() {
        String chinaz = "http://ip.chinaz.com";
        try {
            HttpURLConnection urlConnection = (HttpURLConnection) new URL(chinaz).openConnection();
            InputStream is = urlConnection.getInputStream();
            byte[] bytes = IOUtils.readBytes(is);
            String str = new String(bytes, Charsets.utf8);
            Pattern p = Pattern.compile("\\<dd class\\=\"fz24\">(.*?)\\<\\/dd>");
            Matcher m = p.matcher(str);
            if (m.find()) {
                return m.group(1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
